package com.example.config;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.entity.RestBean;
import com.example.entity.dto.AccountDto;
import com.example.entity.vo.response.AuthorizeVO;
import com.example.filter.JwtAuthorizeFilter;
import com.example.service.AccountService;
import com.example.utils.JwtUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.IOException;
import java.io.PrintWriter;

//springSecurity配置类
@Configuration
public class SecurityConfiguration {

    @Resource
    public JwtUtil jwtUtil;

    //授权过滤器
    @Resource
    public JwtAuthorizeFilter authorizeFilter;

    @Resource
    private AccountService accountService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(config -> config
                        //放行登录接口
                        .requestMatchers("/api/auth/**","/error").permitAll()
                        //验证其他接口
                        .anyRequest().authenticated()
                )
                .formLogin(config -> config
                        .loginProcessingUrl("/api/auth/login")
                        .successHandler(this::onAuthenticationSuccess)
                        .failureHandler(this::onAuthenticationFailure)
                )
                .logout(config -> config
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess)
                )
                .csrf(AbstractHttpConfigurer::disable)
                //设置session为无状态模式，即security不在管session
                .sessionManagement(config -> config
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //添加自定义的登录过滤器
                .addFilterBefore(authorizeFilter, UsernamePasswordAuthenticationFilter.class)
                //配置登录失败处理
                .exceptionHandling(config -> config
                        //未授权处理
                        .authenticationEntryPoint(this::onUnauthorized)
                        //权限不够处理
                        .accessDeniedHandler(this::onAccessDenied)
                )
                .build();
    }

    //自定义登录成功处理
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication)
            throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        //获取用户登录信息
        User userDetail = (User)authentication.getPrincipal();
        //TODO:笨办法，再查一次数据库，聪明办法：自定义UserDetail，不使用框架提供的
        AccountDto accountDto = accountService.findAccountByNameOrEmail(userDetail.getUsername());
        //封装jwt令牌
        String token = jwtUtil.createJwt(userDetail,accountDto.getId(),accountDto.getUsername());
        //返回相应信息（封装成vo）给用户（浏览器）
        AuthorizeVO vo = new AuthorizeVO();
        //Spring提供了对象拷贝
        BeanUtils.copyProperties(accountDto,vo);
//        vo.setUsername(accountDto.getUsername());
        vo.setExpire(jwtUtil.expireTime());
//        vo.setRole(accountDto.getRole());
        vo.setToken(token);
        response.getWriter().write(RestBean.success(vo).asJsonString());
    }

    //自定义登录失败处理
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
    }

    //自定义退出登录成功处理
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();
        //拿到当前请求的token
        String token = request.getHeader("Authorization");
//        DecodedJWT jwt = jwtUtil.resolveJwt(token);
        if(jwtUtil.invalidateJwt(token)){
            writer.write(RestBean.success("logout success!").asJsonString());
        }else {
            writer.write(RestBean.failure(400,"退出登录失败！").asJsonString());
        }

    }

    //自定义未登录处理（大同小异，可以整合到一起）
    public void onUnauthorized(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.unauthorized(authException.getMessage()).asJsonString());
    }

    //自定义权限不足处理
    public void onAccessDenied(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.forbidden(accessDeniedException.getMessage()).asJsonString());
    }
}
